module dgl.graphics.MeshContainer;

import std.conv;
import std.stdio;
import std.string;

import derelict.opengl.gl;

import dgl.graphics.LoadHelper;
import dgl.graphics.Model;
import dgl.graphics.ModelMesh;

import dgl.Matrix;
import dgl.GameTime;

class MeshContainer
{
	private uint m_index;
	
	private string m_name;
	
	private MeshContainer m_parent;
	
	private string m_parentName;
	
	private Matrix m_matLocal;
	
	private Matrix m_matAbsolute;
	
	private Matrix m_matWorld;
	
	private Matrix m_matView;
	
	private Matrix m_matProj;
	
	private ModelMesh m_mesh;	
	
	public uint index()
	{
		return m_index;
	}
	
	public string name()
	{
		return m_name;
	}
	
	public MeshContainer parent()
	{
		return m_parent;
	}
	
	package string parentName()
	{
		return m_parentName;
	}
	
	public Matrix matLocal()
	{
		return m_matLocal;
	}
	
	public Matrix absoluteTransform()
	{
		return m_matAbsolute;
	}
	
	public Matrix matWorld()
	{
		return m_matWorld;
	}
	
	public Matrix matView()
	{
		return m_matView;
	}
	
	public Matrix matProj()
	{
		return m_matProj;
	}
	
	public ModelMesh mesh()
	{
		return m_mesh;
	}
	
	public void index( uint index )
	{
		m_index = index;
	}
	
	public void name( string name )
	{
		m_name = name;
	}
	
	package void parent( MeshContainer parent )
	{
		m_parent = parent;
	}
	
	package void parentName( string parentName )
	{
		m_parentName = parentName;
	}
	
	package void matLocal( Matrix matLocal )
	{
		m_matLocal = matLocal;
	}
	
	public void matWorld( Matrix matWorld )
	{
		m_matWorld = matWorld;
	}
	
	public void matView( Matrix matView )
	{
		m_matView = matView;
	}
	
	public void matProj( Matrix matProj )
	{
		m_matProj = matProj;
	}
	
	package void mesh( ModelMesh mesh )
	{
		m_mesh = mesh;
	}
	
	public this()
	{
		m_index = 0;
		m_matLocal = Matrix.identity;
		m_matWorld = Matrix.identity;
		m_matView = Matrix.identity;
		m_matProj = Matrix.identity;
		m_mesh = null;
		m_name = "";
		m_parent = null;
		m_parentName = "";
	}
	
	public static MeshContainer loadFromFile( LoadHelper loadHelper, Model model )
	{
		string data;
		MeshContainer ret = new MeshContainer();
		
		if( loadHelper.readLine( data ) == "\tIndex" )
		{
			ret.index = toUint( data );
		}
		bool hasParent = false;
		if( loadHelper.readLine( data ) == "\tHasParent" )
		{
			hasParent = cast(bool)( toInt( data ) );
		}
		bool hasFaceUV = false;
		if( loadHelper.readLine( data ) == "\tHasFaceUV" )
		{
			hasFaceUV = cast(bool)( toInt( data ) );
		}
		if( loadHelper.readLine( data ) == "\tName" )
		{
			ret.name = data;
		}
		if( hasParent )
		{
			if( loadHelper.readLine( data ) == "\tParentName" )
			{
				ret.parentName = data;
			}
		}
		else
		{
			ret.parentName = null;
		}
		Matrix matLocal;
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine0" )
		{
			string[] elements = split( data, " " );
			matLocal.m11 = toFloat( elements[0] );
			matLocal.m12 = toFloat( elements[1] );
			matLocal.m13 = toFloat( elements[2] );
			matLocal.m14 = toFloat( elements[3] );
		}
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine1" )
		{
			string[] elements = split( data, " " );
			matLocal.m21 = toFloat( elements[0] );
			matLocal.m22 = toFloat( elements[1] );
			matLocal.m23 = toFloat( elements[2] );
			matLocal.m24 = toFloat( elements[3] );
		}
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine2" )
		{
			string[] elements = split( data, " " );
			matLocal.m31 = toFloat( elements[0] );
			matLocal.m32 = toFloat( elements[1] );
			matLocal.m33 = toFloat( elements[2] );
			matLocal.m34 = toFloat( elements[3] );
		}
		if( loadHelper.readLine( data ) == "\tMatrixLocalLine3" )
		{
			string[] elements = split( data, " " );
			matLocal.m41 = toFloat( elements[0] );
			matLocal.m42 = toFloat( elements[1] );
			matLocal.m43 = toFloat( elements[2] );
			matLocal.m44 = toFloat( elements[3] );
		}
		ret.matLocal = matLocal;
		
		ret.mesh = ModelMesh.loadFromFile( loadHelper, ret, hasFaceUV );
			
		return ret;
	}
	
	public void calculateAbsoluteTransform()
	{
		if( m_parent )
		{
			m_parent.calculateAbsoluteTransform();
			m_matAbsolute = m_matLocal * m_parent.absoluteTransform;
		}
		else
		{
			m_matAbsolute = m_matLocal;
		}
	}
	
	public void draw()
	{
		glMatrixMode( GL_MODELVIEW );
		Matrix modelView = m_matAbsolute * m_matWorld * m_matView;
		glLoadMatrixf( modelView.ptr );
		
		glMatrixMode( GL_PROJECTION );
		glLoadMatrixf( m_matProj.ptr );
		
		m_mesh.draw();
	}
}